using System;
using Microsoft.SPOT;
using Gadgeteer.Modules.PeteBrown;
using GT = Gadgeteer;

namespace GadgeteerMidiTest
{
    public enum SequencerRepeatBehavior
    {
        Once,
        Forever
    }

    // plenty of other options you can implement here
    public enum SequencerDirection
    {
        Forward,
        Backward,
        ForwardAndBackward
    }

    public class SequencerStep
    {
        public MidiChannel Channel { get; set; }
        public byte NoteNumber { get; set; }
        public byte Velocity { get; set; }

        public SequencerStep() { }

        public SequencerStep(MidiChannel channel, byte noteNumber, byte velocity)
        {
            Channel = channel;
            NoteNumber = noteNumber;
            Velocity = velocity;
        }

    }

    public class StepSequencer
    {
        private MidiModule _midiModule;
        private GT.Timer _timer;

        private TimeSpan _stepDuration;
        public TimeSpan StepDuration
        {
            get { return _stepDuration; }
            set
            {
                _stepDuration = value;

                _timer.Interval = _stepDuration;
                _timer.Restart();
            }
        }

        public int CurrentStep { get; set; }
        public SequencerRepeatBehavior RepeatBehavior { get; set; }
        public SequencerDirection Direction { get; set; }
        public int Iterations { get; private set; }

        private SequencerStep[] _steps;

        // TODO: needs things like staccato, legato etc.
        public StepSequencer(MidiModule midiModule, int stepCount, TimeSpan stepDuration)
        {
            _midiModule = midiModule;
            _stepDuration = stepDuration;

            _steps = new SequencerStep[stepCount];

            RepeatBehavior = SequencerRepeatBehavior.Forever;
            Direction = SequencerDirection.Forward;

            CurrentStep = 0;

            _timer = new GT.Timer(stepDuration);
            _timer.Tick += new GT.Timer.TickEventHandler(OnTimerTick);
        }


        public SequencerStep this [int index]
        {
            get { return _steps[index]; }
            set { _steps[index] = value; }
        }

        private void SilenceLastNote()
        {
            // turn off last step
            if (_lastStep != null)
                _midiModule.SendNoteOff(_lastStep.Channel, _lastStep.NoteNumber, 0);
        }

        private void PlayCurrentNote()
        {
            _lastStep = _steps[CurrentStep];

            if (_steps[CurrentStep] != null)
            {
                //Debug.Print("Playing Note " + _steps[CurrentStep].NoteNumber);
                _midiModule.SendNoteOn(_steps[CurrentStep].Channel, _steps[CurrentStep].NoteNumber, _steps[CurrentStep].Velocity);

            }
        }

        public void Start()
        {
            if (_stepDuration != TimeSpan.Zero)
                _timer.Start();
        }

        public void Stop()
        {
            _timer.Stop();

            SilenceLastNote();
        }

        public void Reset()
        {
            _timer.Stop();

            SilenceLastNote();

            CurrentStep = 0;
        }

        private SequencerStep _lastStep = null;

        void OnTimerTick(GT.Timer timer)
        {
            SilenceLastNote();

            switch (Direction)
            {
                case SequencerDirection.Forward:
                    if (CurrentStep + 1 >= _steps.Length)
                        CurrentStep = 0;
                    else
                        CurrentStep += 1;
                    
                    break;

                case SequencerDirection.Backward:
                    if (CurrentStep - 1 < 0)
                        CurrentStep = _steps.Length -1;
                    else
                        CurrentStep -= 1;
                    break;

                case SequencerDirection.ForwardAndBackward:
                    // todo: alternate direction
                    break;
            }

            PlayCurrentNote();

        }



    }
}
